package com.wsx.springai.utils;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.DateUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StreamUtils;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class HttpUtil {

    private static final int DEFAULT_BUFFER_SIZE = 2048;

    private static final int TIMEOUT = 60000;


    private static CloseableHttpClient httpClient = null;

    static{
        // 设置配置请求参数
        RequestConfig requestConfig = RequestConfig.custom().setCircularRedirectsAllowed(false)
                .setSocketTimeout(TIMEOUT)
                .setConnectTimeout(TIMEOUT)// 连接主机服务超时时间
                .setConnectionRequestTimeout(TIMEOUT)// 请求超时时间
                .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                .setMaxRedirects(0)
                .build();

        SSLContext sslContext = null;
        try {
            sslContext = SSLContexts.custom().setProtocol("TLS").loadTrustMaterial(null, (X509Certificate[] x509Certificates, String s) -> true).build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        HostnameVerifier hostnameVerifier = (String s, SSLSession sslSession) -> true;
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", new SSLConnectionSocketFactory(sslContext, hostnameVerifier)).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        cm.setMaxTotal(1024);
        cm.setDefaultMaxPerRoute(1024);
        httpClient = HttpClientBuilder.create().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).build();
    }

    public static void doGetToFile(String url, File file, Map<String, String> headers, List<BasicClientCookie> cookies)  {
        CloseableHttpResponse response = null;
        try {
            // 创建httpGet远程连接实例
            HttpGet httpGet = new HttpGet(url);
            // 设置请求头信息，鉴权
            if(null != headers){
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpGet.addHeader(entry.getKey(), entry.getValue());
                }
            }
            setCookies(cookies, httpGet);
            // 执行get请求得到返回对象
            response = httpClient.execute(httpGet);
            // 通过返回对象获取返回数据
            HttpEntity entity = response.getEntity();
            // 通过EntityUtils中的toString方法将结果转换为字符串
            try(InputStream is = entity.getContent(); OutputStream os = new FileOutputStream(file)){
                byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
                int bytesRead = is.read(buf);
                while(bytesRead != -1){
                    os.write(buf, 0, bytesRead);
                    bytesRead = is.read(buf);
                }
                os.flush();
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static String doGet(String url)  {
        return doGet(url, null, null);
    }
    public static String doGet(String url, Map<String, String> headers, List<BasicClientCookie> cookies)  {
        CloseableHttpResponse response = null;
        try {
            // 通过返回对象获取返回数据
            response = doGetResponse(url, headers, cookies);
            return EntityUtils.toString(response.getEntity());
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public static void downloadFile(String url, File file)  {
        CloseableHttpResponse response = null;
        try {
            // 通过返回对象获取返回数据
            response = doGetResponse(url, null, null);
            HttpEntity entity = response.getEntity();
            StreamUtils.copy(entity.getContent(), new FileOutputStream(file));
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static CloseableHttpResponse doGetResponse(String url, Map<String, String> headers, List<BasicClientCookie> cookies)  {
        CloseableHttpResponse response = null;
        try {
            // 创建httpGet远程连接实例
            HttpGet httpGet = new HttpGet(url);
            // 设置请求头信息，鉴权
            if(null != headers){
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpGet.addHeader(entry.getKey(), entry.getValue());
                }
            }
            setCookies(cookies, httpGet);
            // 执行get请求得到返回对象
            response = httpClient.execute(httpGet);
            return response;
            // 通过返回对象获取返回数据
//            HttpEntity entity = response.getEntity();
//            return EntityUtils.toString(entity);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String postJson(String url, String json, Map<String, String> headers, List<BasicClientCookie> cookies){
        CloseableHttpResponse response = null;
        try {
            HttpPost post = new HttpPost(url);
            post.addHeader("Content-Type", "application/json");
            // 设置请求头信息，鉴权
            if(null != headers){
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    post.addHeader(entry.getKey(), entry.getValue());
                }
            }
            post.setEntity(new StringEntity(json, "UTF-8"));
            setCookies(cookies, post);
            // 执行get请求得到返回对象
            response = httpClient.execute(post);
            // 通过返回对象获取返回数据
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }


    public static String postForm(String uri, Map<String, Object> params, Map<String, String> headers) {
        CloseableHttpResponse response = null;
        HttpPost post = new HttpPost(uri);
        List<BasicNameValuePair> nvp = new ArrayList<>();

        if (null != params && !params.isEmpty()) {
            for (Map.Entry<String, Object> current : params.entrySet()) {
                String value = null;

                if (current.getValue() instanceof Date) {
                    value = DateUtils.formatDate((Date) current.getValue(),
                            "yyyy-MM-dd HH:mm:ss.SSS");
                } else {
                    value = current.getValue() == null ? "" : current.getValue().toString();
                }
                nvp.add(new BasicNameValuePair(current.getKey(), value));
            }
        }

        if (null != headers && !headers.isEmpty()) {
            for (Map.Entry<String, String> current : headers.entrySet()) {
                post.addHeader(current.getKey(), current.getValue());
            }
        }
        try {
            post.setEntity(new UrlEncodedFormEntity(nvp, StandardCharsets.UTF_8));
            response = httpClient.execute(post);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                try {
                    // LOGGER.debug(REQUEST_MSG+ uri +",params = "+
                    // MapUtils.transMapToString(params) + ",result= " +
                    // result);
                    return EntityUtils.toString(entity, StandardCharsets.UTF_8);
                } catch (Exception e) {

                }
            }
        } catch (Exception e) {

        } finally {
            if(null != response){
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    private static void setCookies(List<BasicClientCookie> cookies, HttpRequestBase reqeust){
        if(null == cookies || cookies.size() <= 0){
            return;
        }
        BasicCookieStore cookieStore = new BasicCookieStore();
        for (BasicClientCookie cookie : cookies) {
            cookieStore.addCookie(cookie);
        }
        reqeust.addHeader("Cookie", cookieStore.toString());
    }


    public static InputStream postJsonForStream(String url, String json, Map<String, String> headers, List<BasicClientCookie> cookies){
        CloseableHttpResponse response = null;
        try {
            HttpPost post = new HttpPost(url);
            post.addHeader("Content-Type", "application/json");
            // 设置请求头信息，鉴权
            if(null != headers){
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    post.addHeader(entry.getKey(), entry.getValue());
                }
            }
            post.setEntity(new StringEntity(json, "UTF-8"));
            setCookies(cookies, post);
            // 执行get请求得到返回对象
            response = httpClient.execute(post);
            // 通过返回对象获取返回数据
            HttpEntity entity = response.getEntity();
            return entity.getContent();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
